package com
{
    import flash.display.InteractiveObject;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    
    public class ScrollBarLite {
        
        //controlable assets
        //the dragable rectange in the center of the scroll bar that also shows the user where in the scrolling they are.
        private var scrollFace:Sprite;
        //the top button at the top of the scroll bar that moves scrolls the view to the top of the page
        private var btnUp:InteractiveObject;
        //the bottom button at the bottom of the scroll bar that moves the view to the bottom of the page
        private var btnDown:InteractiveObject;
        //the content that is being scrolled
        private var contentMain:Sprite;
        //the viewport through which the content is being viewed
        private var maskedView:Sprite;
        //the scroll bar
        private var scrollTrack:Sprite;
        //the area where the mouse must be over to pick up MOUSE_WHEEL events.
        private var _mouseWheelTarget:Sprite;
        //the external function that gets called to do scrolling
        private var _onCustomContentScrollProxy:Function;
        //numbers
        private var scrollHeight:Number;
        private var contentHeight:Number;
        private var scrollFaceHeight:Number;
        private var maskHeight:Number;
        private var initPosition:Number;
        private var initContentPos:Number;
        private var finalContentPos:Number;
        private var left:Number;
        private var top:Number;
        private var right:Number;
        private var bottom:Number;
        private var dy:Number;
        private var speed:Number;
        private var moveVal:Number;
        private var scrollWheelSpeed:Number = 20;
        private var minScrollFaceHeight:Number = 10;
        private var orgScrollFaceHeight:Number;

        
        /**
         * Control class for controlling scroll bar graphics
         *  
         * @param scrollFaceParam the dragable rectange in the center of the scroll bar that also shows the user where in the scrolling they are.
         * @param btnUpParam the top button at the top of the scroll bar that moves scrolls the view to the top of the page
         * @param btnDownParam the bottom button at the bottom of the scroll bar that moves the view to the bottom of the page
         * @param contentMainParam the content that is being scrolled
         * @param maskedViewParam the viewport through which the content is being viewed
         * @param scrollTrackParam the scroll bar
         * 
         */
        public function ScrollBarLite(scrollFaceParam:Sprite, btnUpParam:InteractiveObject, btnDownParam:InteractiveObject, contentMainParam:Sprite, maskedViewParam:Sprite, scrollTrackParam:Sprite)
        {
            var scrollTrackRect:Rectangle;
            
            this.scrollFace = scrollFaceParam; 
            this.btnUp = btnUpParam; 
            this.btnDown = btnDownParam;
            this.contentMain = contentMainParam;
            this.maskedView = maskedViewParam;
            this.scrollTrack = scrollTrackParam;
            
            scrollHeight = scrollTrack.height;
            contentHeight = contentMain.height;
            maskHeight = maskedView.height;
            orgScrollFaceHeight = scrollFace.height;
            if (contentHeight < maskHeight) {
                scrollFace.visible = false;
                btnUp.mouseEnabled = false;
                btnDown.mouseEnabled = false;
            } else {
                scrollFace.visible = true;
                btnUp.mouseEnabled = true;
                btnDown.mouseEnabled = true;
                //change the size of the scroll dragger aka scrollFace based on the size of the content vs the viewport
                scrollFace.height = orgScrollFaceHeight*(maskHeight/contentHeight);
                if (minScrollFaceHeight > scrollFace.height){
                    scrollFace.height = minScrollFaceHeight;
                }
            }
            scrollFaceHeight = scrollFace.height;
            initPosition = scrollFace.y = scrollTrack.y;
            initContentPos = contentMain.y;
            finalContentPos = maskHeight-contentHeight+initContentPos;
            scrollTrackRect = scrollTrack.getBounds(scrollTrack.parent);
            left = scrollTrackRect.x;
            top = scrollTrack.y;
            right = scrollTrackRect.x;
            bottom = scrollTrack.height-scrollFaceHeight+scrollTrack.y;
            dy = 0;
            speed = 10;
            moveVal = (contentHeight-maskHeight)/(scrollHeight-scrollFaceHeight);
            //set up listeners
            scrollFace.addEventListener(MouseEvent.MOUSE_DOWN, _onPressScrollFace);
            scrollFace.addEventListener(MouseEvent.MOUSE_UP, _onMouseUpScrollFace);
            btnUp.addEventListener(MouseEvent.MOUSE_DOWN, _onPressBtnUp);
            btnUp.addEventListener(MouseEvent.MOUSE_OUT, _onDragOutBtnUp);
            btnUp.addEventListener(MouseEvent.MOUSE_UP, _onUpBtnUp);
            btnDown.addEventListener(MouseEvent.MOUSE_DOWN, _onPressBtnDown);
            btnDown.addEventListener(MouseEvent.MOUSE_OUT, _onDragOutBtnDown);
            btnDown.addEventListener(MouseEvent.MOUSE_UP, _onUpBtnDown);
            
            //make sure scrollFace is centered over the scrollTrack, you may want to comment this out if you are doing something weird with your scrollFace graphics
            scrollFace.x = scrollTrackRect.x+( (scrollTrack.width - scrollFace.width)/2 );
        }
        /**
         * call this when ever the size of the content changes to that the scroll bar can update 
         * @param artificialHeight if you dont want to base the scroll bar on conent height and instead some other value
         * 
         */
        public function onContentSizeChange(artificialHeight:Number = -1):void{
            scrollHeight = scrollTrack.height;
            if (artificialHeight != -1){
                contentHeight = artificialHeight;
            } else {
                contentHeight = contentMain.height;
            }
            
            maskHeight = maskedView.height;

            if (contentHeight < maskHeight) {
                scrollFace.visible = false;
                btnUp.mouseEnabled = false;
                btnDown.mouseEnabled = false;
                scrollFace.height = orgScrollFaceHeight;
                scrollFace.y = scrollTrack.y;
                contentMain.y = initContentPos;
                _removeListener(_mouseWheelTarget, MouseEvent.MOUSE_WHEEL, _onMouseWheel);
                _removeListener(_mouseWheelTarget, MouseEvent.MOUSE_OUT, _onMouseUpScrollFace);
            } else {
                scrollFace.visible = true;
                btnUp.mouseEnabled = true;
                btnDown.mouseEnabled = true;
                //change the size of the scroll dragger aka scrollFace based on the size of the content vs the viewport
                scrollFace.height = orgScrollFaceHeight*(maskHeight/contentHeight);
                if (minScrollFaceHeight > scrollFace.height){
                    scrollFace.height = minScrollFaceHeight;
                }
                if ( !_mouseWheelTarget.hasEventListener(MouseEvent.MOUSE_WHEEL) ){
                    _mouseWheelTarget.addEventListener(MouseEvent.MOUSE_WHEEL, _onMouseWheel);
                    _mouseWheelTarget.addEventListener(MouseEvent.MOUSE_OUT, _onMouseUpScrollFace);
                }
            }
            scrollFaceHeight = scrollFace.height;
            bottom = scrollTrack.height-scrollFaceHeight+scrollTrack.y;
            finalContentPos = maskHeight-contentHeight+initContentPos;
            moveVal = (contentHeight-maskHeight)/(scrollHeight-scrollFaceHeight);
        }
        /**
         * run this function if you want to enable the mouse wheel when the mouse is over the target area.
         * @param target the area that picks up MOUSE_WHEEL events
         * 
         */
        public function registerMouseWheelTarget(target:Sprite):void{
            target.addEventListener(MouseEvent.MOUSE_WHEEL, _onMouseWheel);
            target.addEventListener(MouseEvent.MOUSE_OUT, _onMouseUpScrollFace);
            _mouseWheelTarget = target;
        }
        /**
         * if you don't want the scroll bar to work by changing the y value of the content and instead do something else you can register your own handler 
         * @param callback should look something like the following onScrollCallBack(xLocOfContent:Number)
         * 
         */
        public function registerCustomContentScroll(callback:Function):void{
            _onCustomContentScrollProxy = callback;
        }
        /**
         * Removes all listeners that are currently running so that unused scroll bar components can be garbage collected.
         */
        public function destroy():void{
            _removeListener(scrollFace, MouseEvent.MOUSE_DOWN, _onPressScrollFace);
            _removeListener(scrollFace, MouseEvent.MOUSE_UP, _onMouseUpScrollFace);
            _removeListener(btnUp, MouseEvent.MOUSE_DOWN, _onPressBtnUp);
            _removeListener(btnUp, MouseEvent.MOUSE_OUT, _onDragOutBtnUp);
            _removeListener(btnUp, MouseEvent.MOUSE_UP, _onUpBtnUp);
            _removeListener(btnUp, Event.ENTER_FRAME, _onEnterFrameBtnUp);
            _removeListener(btnDown, MouseEvent.MOUSE_DOWN, _onPressBtnDown);
            _removeListener(btnDown, MouseEvent.MOUSE_OUT, _onDragOutBtnDown);
            _removeListener(btnDown, MouseEvent.MOUSE_UP, _onUpBtnDown);
            _removeListener(btnDown, Event.ENTER_FRAME, _onEnterFrameBtnDown);
            _removeListener(_mouseWheelTarget, MouseEvent.MOUSE_WHEEL, _onMouseWheel);
            _removeListener(_mouseWheelTarget, MouseEvent.MOUSE_OUT, _onMouseUpScrollFace);
        }
        private function _onMouseWheel(e:MouseEvent):void{
            var d:Number;
            var delta:Number = e.delta;
             
            if (delta > 1){
                delta = 1;
            }
            if (delta < -1){
                 delta = -1;
            } 
            d = -delta * scrollWheelSpeed;

            if (d > 0) {
                scrollFace.y = Math.min(bottom, scrollFace.y+d);
            }
            if (d < 0) {
                scrollFace.y = Math.max(top, scrollFace.y+d);
            }
            _updateContentPos();

        }
        private function _updateContentPos():void {
            dy = Math.abs(initPosition-scrollFace.y);
            if (_onCustomContentScrollProxy == null){
                contentMain.y = Math.round(dy*-1*moveVal+initContentPos);
            } else {
                try{
                    _onCustomContentScrollProxy(Math.round(dy*-1*moveVal+initContentPos));
                } catch(e:Error){
                    trace("_onCustomContentScrollProxy callback failed : "+e.message);
                }
            }
        }

        private function _onPressScrollFace(e:MouseEvent):void {
            var currPos:Number = scrollFace.y;
            //startDrag(this, false, left, top, right, bottom);
            scrollFace.startDrag(   false, new Rectangle(  scrollTrack.x+( (scrollTrack.width-scrollFace.width)/2 ), top, 0, bottom-top  )   );
            scrollFace.addEventListener(MouseEvent.MOUSE_MOVE, _onMouseMoveScrollFace);
        }
        private function _onMouseMoveScrollFace(e:MouseEvent):void{
            //dy = Math.abs(initPosition-scrollFace.y);
            //contentMain.y = Math.round(dy*-1*moveVal+initContentPos);
            _updateContentPos();
        }
        private function _onDragOutBtnDown(e:MouseEvent):void {
            btnDown.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown);
        }
        private function _onDragOutBtnUp(e:MouseEvent):void {
            btnUp.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp);
        }
        private function _onMouseUpScrollFace(e:MouseEvent):void {
            scrollFace.stopDrag();
            //delete this.onMouseMove;
            //trace(e.relatedObject);
            scrollFace.removeEventListener(MouseEvent.MOUSE_MOVE, _onMouseMoveScrollFace);
        }
        private function _onPressBtnUp(e:MouseEvent):void {
            btnUp.addEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp);
        }
        private function _onUpBtnUp(e:MouseEvent):void {
            btnUp.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp);
        }
        private function _onEnterFrameBtnUp(e:Event):void {
            
            if (contentMain.y+speed<maskedView.y) {
                if (scrollFace.y<=top) {
                    scrollFace.y = top;
                } else {
                    scrollFace.y -= speed/moveVal;
                }
                //contentMain.y += speed;
            } else {
                scrollFace.y = top;
                //contentMain.y = maskedView.y;
                //delete this.onEnterFrame;
                btnUp.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnUp);
            }
            _updateContentPos();
        }
        private function _onPressBtnDown(e:MouseEvent):void {
            btnDown.addEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown);
        }
        private function _onUpBtnDown(e:MouseEvent):void {
            btnDown.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown);
        }
        private function _onEnterFrameBtnDown(e:Event):void{
            if ( contentMain.y-speed > finalContentPos ) {
                if (scrollFace.y >= bottom) {
                    scrollFace.y = bottom;
                } else {
                    scrollFace.y += speed/moveVal;
                }
                //contentMain.y -= speed;
            } else {
                scrollFace.y = bottom;
                //contentMain.y = finalContentPos;
                //delete this.onEnterFrame;
                btnDown.removeEventListener(Event.ENTER_FRAME, _onEnterFrameBtnDown);
            }
            _updateContentPos();
        }
        private function _removeListener(object:EventDispatcher, eventStr:String, callback:Function):void{
            if ( object && object.hasEventListener(eventStr) ){
                object.removeEventListener(eventStr, callback);
            }
        }
    }
}
